package com.liukunup.util;

import com.google.gson.Gson;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.util.*;

/**
 * HTTP 工具类
 * @author Liu Kun
 * @date 2021-04-04 00:29:32
 **/
@Slf4j
public class OkHttpUtil {

    private OkHttpClient okHttpClient;
    private static OkHttpUtil mInstance;
    private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();

    private OkHttpUtil() {
        okHttpClient = new OkHttpClient.Builder().cookieJar(new CookieJar() {

            @Override
            public void saveFromResponse(@NotNull HttpUrl url, @NotNull List<Cookie> cookies) {
                cookieStore.put(url.host(), cookies);
            }

            @NotNull
            @Override
            public List<Cookie> loadForRequest(@NotNull HttpUrl url) {
                List<Cookie> cookies = cookieStore.get(url.host());
                return cookies != null ? cookies : new ArrayList<Cookie>();
            }
        }).build();
    }

    /**
     * 单例模式
     * 创建 OkHttpUtil
     * @return mInstance
     */
    public static OkHttpUtil getInstance() {
        if (mInstance == null) {
            synchronized (OkHttpUtil.class) {
                mInstance = new OkHttpUtil();
            }
        }
        return mInstance;
    }

    /**
     * 构建 Get 请求
     * @param url     链接地址
     * @param headers 请求头
     * @return 请求对象
     */
    private Request buildGetRequest(@NonNull String url, Map<String, String> headers) {
        Request.Builder builder = new Request.Builder();
        builder.get();
        builder.url(url);
        if (null != headers && !headers.isEmpty()) {
            builder.headers(Headers.of(headers));
        }
        Request request = builder.build();
        log.debug("Request: {}", request);
        return request;
    }

    /**
     * 构建 Post Json 请求
     * @param url     链接地址
     * @param headers 请求头
     * @param jsonStr 请求体
     * @return 请求对象
     */
    private Request buildPostJsonRequest(@NonNull String url, Map<String, String> headers, String jsonStr) {
        Request.Builder builder = new Request.Builder();
        builder.url(url);
        if (null != headers && !headers.isEmpty()) {
            builder.headers(Headers.of(headers));
        }
        if (!StringUtils.isEmpty(jsonStr)) {
            MediaType mediaType = MediaType.parse("application/json; charset=UTF-8");
            RequestBody body = RequestBody.create(mediaType, jsonStr);
            builder.post(body);
        }
        Request request = builder.build();
        log.debug("Request: {}", request);
        return request;
    }

    /**
     * 执行同步请求
     * @param request 请求
     * @return 响应
     */
    private Response doSyncRequest(Request request) {
        Response response = null;
        try {
            response = okHttpClient.newCall(request).execute();
        } catch (IOException e) {
            log.error("doSyncRequest: {}", e.getMessage());
        }
        log.debug("Response: {}", response);
        return response;
    }

    /**
     * 执行异步请求
     * @param request  请求
     * @param callback 响应回调
     */
    private void doAsyncRequest(Request request, Callback callback) {
        okHttpClient.newCall(request).enqueue(callback);
    }

    /**
     * 执行 Get 请求
     * @param url     地址
     * @param headers 请求头
     * @return 响应对象
     */
    private Response syncGet(@NonNull String url, Map<String, String> headers) {
        Request request = getInstance().buildGetRequest(url, headers);
        return getInstance().doSyncRequest(request);
    }

    /**
     * 执行 Get 请求
     * @param url     地址
     * @param headers 请求头
     * @return 响应对象
     */
    public static String doSyncGet(@NonNull String url, Map<String, String> headers) {
        Response response = getInstance().syncGet(url, headers);
        return getResponseStr(response);
    }

    /**
     * 执行 Get 请求
     * @param classOfT 输出对象类型
     * @param url      地址
     * @param headers  请求头
     * @return 响应对象
     */
    public static <T> T doSyncGet(Class<T> classOfT, @NonNull String url, Map<String, String> headers) {
        String responseStr = doSyncGet(url, headers);
        return new Gson().fromJson(responseStr, classOfT);
    }

    /**
     * 执行 Post Json 请求
     * @param url     地址
     * @param headers 请求头
     * @param jsonStr 请求体
     * @return 请求对象
     */
    private Response syncPostJson(@NonNull String url, Map<String, String> headers, String jsonStr) {
        Request request = getInstance().buildPostJsonRequest(url, headers, jsonStr);
        return getInstance().doSyncRequest(request);
    }

    /**
     * 执行 Post Json 请求
     * @param url     地址
     * @param headers 请求头
     * @param jsonStr 请求体
     * @return 请求对象
     */
    public static String doSyncPostJson(@NonNull String url, Map<String, String> headers, String jsonStr) {
        Response response = getInstance().syncPostJson(url, headers, jsonStr);
        return getResponseStr(response);
    }

    /**
     * 执行 Post Json 请求
     * @param url     链接地址
     * @param headers 请求头
     * @param jsonObj 请求体
     * @return 请求对象
     */
    public static String doSyncPostJson(@NonNull String url, Map<String, String> headers, Object jsonObj) {
        return doSyncPostJson(url, headers, new Gson().toJson(jsonObj));
    }

    /**
     * 执行 Post Json 请求
     * @param classOfT 输出对象类型
     * @param url     链接地址
     * @param headers 请求头
     * @param jsonStr 请求体
     * @return 请求对象
     */
    public static <T> T doSyncPostJson(Class<T> classOfT,
                                       @NonNull String url, Map<String, String> headers, String jsonStr) {
        String responseStr = doSyncPostJson(url, headers, jsonStr);
        return new Gson().fromJson(responseStr, classOfT);
    }

    /**
     * 执行 Post Json 请求
     * @param classOfT 输出对象类型
     * @param url     链接地址
     * @param headers 请求头
     * @param jsonObj 请求体
     * @return 请求对象
     */
    public static <T> T doSyncPostJson(Class<T> classOfT,
                                       @NonNull String url, Map<String, String> headers, Object jsonObj) {
        return doSyncPostJson(classOfT, url, headers, new Gson().toJson(jsonObj));
    }

    /**
     * 获取响应字符串
     * @param response 响应对象
     * @return 响应字符串
     */
    private static String getResponseStr(Response response) {
        if (null == response) {
            log.error("The response is null.");
            return null;
        }
        if (!response.isSuccessful()) {
            log.error("Response: {}", response);
            return null;
        }
        String responseStr = null;
        try {
            responseStr = Objects.requireNonNull(response.body()).string();
        } catch (IOException e) {
            log.error("doSyncGet: {}", e.getMessage());
        }
        log.debug("Response: {}", responseStr);
        return responseStr;
    }
}
